今天要來聊聊這個主題的主角 隱含的強制轉型
,
如果還沒看過前兩篇的可以斟酌觀看~
以下連結奉上~
【這些年我似是非懂的 Javascript】Day 8 - 魔幻邪惡的強制轉型 #第一章 # 心情轉折
【這些年我似是非懂的 Javascript】Day 9 - 魔幻邪惡的強制轉型 #第二章 #明確的強制轉型
那我們就開始今天的內容~
圖片來源
假使說你用的是一個強型別的語言,並且想將x
要從 A type 轉到 C type 並存到 y
,但是這中間必須先轉成 B type,如以下範例。
CType x = CType(BType(y));
假使說那個語言可以簡化定義使用方式可以用以下的方法實作
CType x = CType(y);
簡化了了先轉換 B Type 這個動作,增加可讀性但卻省略了中間隱藏了那些轉換的細節,你會那麼在乎這中間省略的步驟嗎?
我個人感覺會偏向可以省略成下面的方案,
對我來說如果知情就沒什麼好怕的了。
我們可以選用他好的一面,並且熟悉他去避開他可能會發生的事情,我覺得這滿重要的,一蓋的去否定反而減少了該語言好的特性。
這章節我們就來好好與它做朋友
昨天講過明確的強轉型關於 String 和 Number ,
這邊也是同個主題但是主角是"隱含的"。
先來看一下以下範例
const a = '16';
const b = '1';
const c = 16;
const d = 1;
a+b // 161
c+d // 17
為什麼會有像是以上的結果?
簡單來說就是運算元的部分是否有一個或是兩個 String ,而這邊的 +
就會以為要是 String 的串接。
看過前幾篇的你一定知道我接著要說什麼...
上面說的部分是錯的哦!
如果上面說的是對的話那該如何解釋下面這個範例
const a = [1,2];
const b = [3,4];
a+b; // "1,23,4"
這明顯已經將兩個都不是 String,但是卻將兩邊轉成 String 並且串接起來了。
依據 ES5 語言規格的 section 11.6.1,如果有其中一個運算元是 String 或是接下來的任一步驟會產生 String 的值時用 +
就會進行 String 的串接,那 +
所用的演算法又剛好對應到剛剛用的 Array ,當 +
遇到一個 Object (包含 Array),作為任一運算元時,他會先呼叫 ToPrimitive 抽象運算來先處理該值,然後接著呼叫 [[DefaultValue]]
,並依照上下文情境傳入 number 來作為 hint 偏好型別。 (這邊的上下文情境其實我也不太懂 Orz)
接著又依據 ToNumber 的抽象運算處理 object ,當他沒辦法在 array 進行 valueOf()
產生簡單的基值,它就會改使用 toString()
表示法,導致他變成以下的結果
"1,2"+"3+4"; // 1,23,4
看不懂?
大致上來說就是
今天 +
看到他左右手其中一個是 Object 他會想先把他轉成 Number 然後 ToNumber 看不懂...就給了 toString() 處理。
書中提到在使用 JS 日常中最常看到的是以下範例
const a = 19;
const c = a+"";
c // "19"
他強制轉型成 String,因為剛剛提到的第一點是他先看兩邊運算元其中有一個 String 他就會判定要進行字串相加。
那如果想要轉回來怎麼辦?
與他對立的 -
出現了xD
直接看範例
const a = "19";
const c = a-0;
c // 19
-
這個運算子只被定義用來做數值的相減,
所以會迫使 a 強迫轉型成 Number。
*
和/
也有相同效果
那如果將剛剛的範例再一次並且使用 -
呢?
器跨買~
const a = [1,2];
const b = [3,4];
a-b; // NaN
他們會先將 array 的值都會先被強制轉型成 String 然後在變成 number,最後才會進行 -
的動作。
也就是你把他再變成那再簡單一點
const a = [1];
const b = [2];
a-b; // -1
登愣~
結果就如你所願。
我個人也覺得非常有趣書中提到的一點是,儘管社群裡在怎麼被說邪惡啊魔幻,但相比 a = b +""
的用法卻比 String(..)
這種明確的用法來的常見且實用 xDD
那我就問你!
你還憤怒嗎?如果是那就是你還害怕 (開玩笑的)
看書上說 (完全撇除責任xD)
強制轉型能發光發熱的地方在於將某個複雜的 boolean 邏輯簡化成簡單的數值加法運算。
(這不是通用技巧,只是單純看情況的專用解法)
來看看下面的範例的情境
function onlyOne(a,b,c){
return !!((a && !b && !c) || (!a && b && !c) || (!a && !b && c) )
};
const a = true;
const b = false;
onlyOne(a,b,b); // true
onlyOne(b,a,b); // true
onlyOne(a,b,a); // false
簡單來說上面的範例想要做的是參數中只有一個為 true 或 truthy 就回傳 true,反之如果有兩個以上就是 false,那如果這個範例改成處理很多很多很多個值怎麼辦?
這時候就可以展現真正的技術了(誤
function onlyOne(){
let sum = 0;
for (let v of arguments){
if (v) sum+= v;
}
return sum == 1;
};
const a = true;
const b = false;
onlyOne(a,b,b); // true
onlyOne(b,a,b); // true
onlyOne(b,b,b,a,b); // true
onlyOne(a,b,a); // false
onlyOne(a,a,b,a,b,b,b,a); // false
簡單來說就是讓隱含的強制轉型將所有的 Boolean 變成 0
或是 1
,並且將所有的參數都相加,如果最終結果等於 1
回傳 true,否則回傳 false,完全符合剛剛的需求。
什麼時候的運算式會需要隱含的強制轉型?
其實超常見的
以上那些就算不是 Boolean 也會被強制轉型成 Boolean。
今天是第十天,
剩餘完賽還有二十天~
今天早上睡過頭了黃金讀書時間破滅xDD
但後續還是照著原本的行程走~
以上為今天分享的內容
希望對你有幫助
感謝您的收看
我們明天繼續分享"魔幻邪惡的強制轉型系列最終章!"
我們明天見~
你所不知道的 JS|導讀,型別與文法 (You Don't Know JS: Up & Going)